package fr.upmc.dtgui.reflexion;

import java.lang.reflect.Modifier;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;

/**
 * <b>RobotActuatorCommandFactory est la classe Factory qui va créer les 
 * sous-classes de RobotActuatorCommand, permettant la manipulation du robot par
 * la station de téléopération.</b>
 * <p>
 * Cette classe va créer deux sous-classes de RobotActuatorCommand via deux
 * méthodes différentes:
 * <ul>
 * <li>createSpeedChange() va créer la classe SpeedChange</li>
 * <li>createSteeringChange() va créer la classe SteeringChange</li>
 * </ul>
 * </p>
 * 
 * @see fr.upmc.dtgui.robot.RobotActuatorCommand
 * 
 * @author Ayoub Benali, Maxime Sarilar
 */
public class RobotActuatorCommandFactory {
	
	/**
     * La méthode createSpeedChange.
     * <p>
     * Cette méthode se charge de créer lors du chargement du robot la classe
     * SpeedChange qui permettra à la station de téléopération de commander la 
     * vitesse du robot.
     * </p>
     * 
     * @param pool
     *            Le ClassPool associé à la classe chargée.
     * @param ct
     *            La CtClass du robot.
     * @return
     * 			  La CtClass SpeedChange générée.
     */
	public static CtClass createSpeedChange(CtClass ct, ClassPool pool)
			throws Exception {
		CtClass speedChange = ct.makeNestedClass("SpeedChange", true);
		speedChange.setSuperclass(pool
				.getCtClass("fr.upmc.dtgui.robot.RobotActuatorCommand"));

		//Ajout du champ:
		CtField speed = new CtField(CtClass.doubleType, "speed", speedChange);
		speedChange.addField(speed);

		//Ajout du constructeur:
		CtConstructor speedChangeConstructor = new CtConstructor(
				new CtClass[] { CtClass.doubleType }, speedChange);
		String speedChangeConstructorBody = "{" + "super();" + "$0.speed = $1;"
				+ "}";
		speedChangeConstructor.setBody(speedChangeConstructorBody);
		speedChange.addConstructor(speedChangeConstructor);

		//La méthode suivante permettra d'agir effectivement sur le robot:
		CtMethod performOn = new CtMethod(CtClass.voidType, "performOn",
				new CtClass[] { pool
						.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot") },
				speedChange);
		performOn.setBody("{((" + ct.getName() + ")$1).setSpeed($0.speed) ;}");
		speedChange.addMethod(performOn);
		speedChange.setModifiers(Modifier.PUBLIC);
		
		/*
		 * On finalise la classe pour garantir qu'elle ne sera pas modifiée
		 * passé cette méthode:
		 */
		speedChange.toClass();
		return speedChange;
	}

	/**
     * La méthode createSteeringChange.
     * <p>
     * Cette méthode se charge de créer lors du chargement du robot la classe
     * SteeringChange qui permettra à la station de téléopération de commander 
     * l'angle directionnel du robot.
     * </p>
     * 
     * @param pool
     *            Le ClassPool associé à la classe chargée.
     * @param ct
     *            La CtClass du robot.
     * @return
     * 			  La CtClass SteeringChange générée.
     */
	public static CtClass createSteeringChange(CtClass ct, ClassPool pool)
			throws Exception {
		CtClass steeringChange = ct.makeNestedClass("SteeringChange", true);
		steeringChange.setSuperclass(pool
				.getCtClass("fr.upmc.dtgui.robot.RobotActuatorCommand"));

		//Ajout du champ:
		CtField steeringAngle = new CtField(CtClass.doubleType,
				"steeringAngle", steeringChange);
		steeringAngle.setModifiers(Modifier.PUBLIC);
		steeringChange.addField(steeringAngle);

		//Ajout du constructeur:
		CtConstructor steeringChangeConstructor = new CtConstructor(
				new CtClass[] { CtClass.doubleType }, steeringChange);
		steeringChangeConstructor
				.setBody("{ super(); $0.steeringAngle = $1; }");
		steeringChange.addConstructor(steeringChangeConstructor);

		//La méthode suivante permettra d'agir effectivement sur le robot:
		CtMethod performOn = new CtMethod(CtClass.voidType, "performOn",
				new CtClass[] { pool
						.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot") },
				steeringChange);
		performOn.setBody("{((" + ct.getName()
				+ ")$1).setSteeringAngle($0.steeringAngle) ;}");
		steeringChange.addMethod(performOn);
		steeringChange.setModifiers(Modifier.PUBLIC);
		
		/*
		 * On finalise la classe pour garantir qu'elle ne sera pas modifiée
		 * passé cette méthode:
		 */
		steeringChange.toClass();
		return steeringChange;
	}
}
